package com.huiyeji.sort;

import com.huiyeji.sort.utils.OrderUtil;

import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;

public class ObjectMatcher<Source,Rule,Destination extends Matchable<Rule>>{

    private final Set<Node<Rule,Destination>> nodes = new TreeSet<>(new SortedComparator<>());

    private final MatchRule<Source,Rule> rule;

    private MatchFailureHandler<Source,Destination> matchFailureHandler;

    public ObjectMatcher(MatchRule<Source,Rule> rule, MatchFailureHandler<Source,Destination> matchFailureHandler, Collection<Destination> objects){
        this.rule = rule;
        this.matchFailureHandler = matchFailureHandler;
        if (objects != null && objects.size() > 0){
            for (Destination object : objects) {
                if (object != null){
                    nodes.add(new Node<>(object));
                }
            }
        }
    }

    public ObjectMatcher (MatchRule<Source,Rule> rule,MatchFailureHandler<Source,Destination> matchFailureHandler){
        this(rule,matchFailureHandler,null);
    }

    public ObjectMatcher (MatchRule<Source,Rule> rule,Collection<Destination> objects){
        this(rule,null,objects);
    }

    public ObjectMatcher (MatchRule<Source,Rule> rule){
        this(rule,null,null);
    }

    public void registerMatchFailureHandler(MatchFailureHandler<Source,Destination> matchFailureHandler) {
        this.matchFailureHandler = matchFailureHandler;
    }

    public Destination matchObject(Source source){
        for (Node<Rule, Destination> node : this.nodes) {
            if (this.rule.match(source, node.rule)){
                return node.object;
            }
        }
        return matchFailureHandler != null ? matchFailureHandler.handle(source) : null;
    }

    public ObjectMatcher<Source,Rule,Destination> registerObject(Destination object){
        this.nodes.add(new Node<>(object));
        return this;
    }

    public ObjectMatcher<Source,Rule,Destination> registerObject(int order,Destination object){
        this.nodes.add(new Node<>(order,object));
        return this;
    }

    public void registerObject(Destination... objects){
        if (objects != null){
            for (Destination object : objects) {
                if (object != null){
                    nodes.add(new Node<Rule,Destination>(object));
                }
            }
        }
    }

    private static class Node<Rule,Destination> implements Sortable{
        private final Rule rule;

        private final Destination object;

        private final int order;

        public Node(Destination object){
            this(OrderUtil.getOrder(object),object);
        }

        public Node(int order,Destination object){
            this.object = object;
            Matchable<Rule> matchable = (Matchable<Rule>) object;
            this.rule = matchable.ruleInitialize();
            this.order = order;
        }

        @Override
        public int getOrder() {
            return order;
        }
    }
}
